﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using HtmlAgilityPack;
using System.Net;
using System.IO;
using System.Xml.Serialization;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading;
using System.Reflection;
using System.Printing;
using MtgLib;
using GeneralTools;
using System.Diagnostics;
using System.Drawing.Drawing2D;
using iTextSharp;

namespace MtgPrinter
{
    public partial class Form1 : Form
    {
        private WorkerThreadStuff wt;
        private List<string> recentDecks = new List<string>();
        private CacheInMemory<string, Image> memCache = new CacheInMemory<string, Image>();

        private LoadingOverlay loadingOverlay1;

        public Form1()
        {
            InitializeComponent();
            loadingOverlay1 = new LoadingOverlay(pictureBox1);
            loadingOverlay1.LoadingMessage = "Downloading...";
            loadingOverlay1.LoadingFont = new Font(SystemFonts.DefaultFont, FontStyle.Italic);
            loadingOverlay1.LoadingMessageBrush = Brushes.Black;
            loadingOverlay1.ShadingBrush = new SolidBrush(Color.FromArgb(120, Color.LightGray));
            loadingOverlay1.UpdateInterval = 150;
            loadingOverlay1.RotationAngleQuant = 360 / 8;

            memCache.MaxCacheItems = 10;

            wt = new WorkerThreadStuff(Program.AppConfig.NumberOfDownloadingThreads);
            wt.RequestHandled += new WorkerThreadStuff.RequestHandledHandler(wt_RequestHandled);
            wt.RequestRoutine = new WorkerThreadStuff.RequestRoutineCallback(DownloaderRoutine);

            //config
            ReadConfiguration();
        }


        private void Form1_Load(object sender, EventArgs e)
        {
            if (!Directory.Exists(getWorkpath())) return;
            string filename = getWorkpath() + "saved.xml";
            var deck = loadDeck(filename);
            if (deck != null) setDeck(deck);

            LoadRecentFilesList();
            //timer1.Start();
        }


        private void ReadConfiguration()
        {
            if (Program.AppConfig == null) return;
            MtgCardDownloader.DiskCachePath = getWorkpath() + "Cache\\";
            MtgCardDownloader.AllowDiskCaching = Program.AppConfig.AllowDiskCaching;
            MtgCardDownloader.DiskCacheMaxSize = Program.AppConfig.MaxCacheSize;
            MtgCardDownloader.DiskCacheRemoveAfter = new TimeSpan(Program.AppConfig.RemoveFromCacheAfter, 0, 0, 0);
            MtgCardDownloader.DownloadPicturesFrom = Program.AppConfig.DownloadPicturesFrom;

            wt.MaxNumThreads = Program.AppConfig.NumberOfDownloadingThreads;

        }

        private object DownloaderRoutine(object[] request)
        {
            try
            {
                string req = request[0] as string;
                Image img = MtgCardDownloader.GetCardImage(req);
                return img;
            }
            catch (WebException wex)
            {
                MessageBox.Show("Error while downloading " + request[0].ToString() + "\n" + wex.Message, "Web Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return null;
            }
        }

        private delegate void GetCardPictureAsyncHandledCallback(object[] request, object result);
        void wt_RequestHandled(object[] request, object result)
        {
            if (!this.InvokeRequired)
            {
                string req = request[0] as string;
                Image img = result as Image;

                memCache.SaveToCache(req, img);

                if (dataGridView1.SelectedRows.Count > 0)
                {
                    object selected = dataGridView1.SelectedRows[0].Cells[0].Value;
                    if (selected == null || selected == null && req == null || (selected is string && ((string)selected).Length == 0))
                    {
                        setCardPicture(MtgPrinter.Properties.Resources.nopicture);
                    }
                    else
                    {
                        string cardName = selected as string;
                        if (cardName.Equals(req, StringComparison.InvariantCultureIgnoreCase))
                        {
                            loadingOverlay1.PaintLoadingAnimation = false;
                            if (img != null) setCardPicture(img);
                            else setCardPicture(MtgPrinter.Properties.Resources.nopicture);
                        }
                    }
                }
            }
            else
            {
                GetCardPictureAsyncHandledCallback cb = new GetCardPictureAsyncHandledCallback(wt_RequestHandled);
                this.Invoke(cb, new object[] { request, result });
            }
        }

        private void getCardPictureAsync(string cardName)
        {
            Image tmpimg = null;

            if (string.IsNullOrEmpty(cardName))
            {
                wt_RequestHandled(new object[] { cardName }, MtgPrinter.Properties.Resources.nopicture);
                loadingOverlay1.PaintLoadingAnimation = false;

                return;
            }
            //try from memcache
            if (memCache.GetItemFromCache(cardName, out tmpimg))
            {
                wt_RequestHandled(new object[] { cardName }, tmpimg);
                return;
            }
            //try from disk cache
            if ((tmpimg = MtgCardDownloader.getImageFromCache(cardName)) != null)
            {
                wt_RequestHandled(new object[] { cardName }, tmpimg);
                return;
            }

            loadingOverlay1.PaintLoadingAnimation = true;
            setCardPicture(MtgPrinter.Properties.Resources.nopicture);

            wt.AddRequest(new object[] { cardName });
        }

        private Image getCardPicture(string cardName)
        {
            try
            {
                Image tmpimg = null;
                //try from memcache
                if (memCache.GetItemFromCache(cardName, out tmpimg))
                {
                    return tmpimg;
                }

                tmpimg = MtgCardDownloader.GetCardImage(cardName);
                memCache.SaveToCache(cardName, tmpimg);
                if (tmpimg != null)
                {
                    return tmpimg;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.StackTrace, ex.Message);
            }
            return null;
        }

        private void parseAlot(string text)
        {
            text = text.Replace('’', '\'');
            Regex karta = new Regex("[^\\d]*(\\d+)[\\sx]+([\\w \\-\\',:]+)\\W?");
            dataGridView1.Rows.Clear();
            foreach (Match Tag in karta.Matches(text))
            {
                if (Tag.Groups.Count > 2)
                {
                    int quant = int.Parse(Tag.Groups[1].Value.ToString());
                    string cardname = Tag.Groups[2].Value;
                    //getCard(cardname,quant);
                    addListItem(cardname, quant);
                }
            }
        }

        private void addListItem(string cardName, int quantity)
        {
            bool contains = false;
            cardName = CultureInfo.GetCultureInfo("en").TextInfo.ToTitleCase(cardName.Trim());
            foreach (DataGridViewRow row in dataGridView1.Rows)
            {
                if (row.Cells[0] != null && row.Cells[0].Value != null && row.Cells[0].Value.Equals(cardName))
                {
                    contains = true;
                    break;
                }
            }

            if (!contains)
                dataGridView1.Rows.Add(new object[] { cardName, quantity });
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Napewno?", "Sure?", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                dataGridView1.Rows.Clear();
            }
        }

        private string getWorkpath()
        {
            return Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\MtgPrinter\\";
        }

        private void setCardPicture(Image img)
        {
            SetControlPropertyValue(pictureBox1, "Image", img);
        }

        delegate void SetControlValueCallback(Control oControl, String propName, Object propValue);
        private void SetControlPropertyValue(Control oControl, String propName, Object propValue)
        {
            try
            {
                if (oControl.InvokeRequired)
                {
                    SetControlValueCallback d = new SetControlValueCallback(SetControlPropertyValue);
                    oControl.Invoke(d, new object[] { oControl, propName, propValue });
                }
                else
                {
                    Type t = oControl.GetType();
                    PropertyInfo[] props = t.GetProperties();
                    foreach (PropertyInfo p in props)
                    {
                        if (p.Name.ToUpper() == propName.ToUpper())
                        {
                            p.SetValue(oControl, propValue, null);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                using (ExceptionForm ef = new ExceptionForm(e))
                {
                    ef.ShowDialog();
                }
            }

        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                if (!Directory.Exists(getWorkpath())) Directory.CreateDirectory(getWorkpath());

                string filename = getWorkpath() + "saved.xml";

                saveDeck(filename, parseData());

                SaveRecentFilesList();

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.StackTrace, ex.Message);
            }
        }

        public List<DeckEntry> parseData()
        {
            List<DeckEntry> mylist = new List<DeckEntry>();

            foreach (DataGridViewRow row in dataGridView1.Rows)
            {
                if (row.Cells.Count < 2 || row.Cells[0].Value == null || row.Cells[1].Value == null) continue;
                string name = row.Cells[0].Value.ToString();
                int quant = int.Parse(row.Cells[1].Value.ToString());

                mylist.Add(new DeckEntry(name, quant));
            }

            return mylist;
        }

        public void saveDeck(string path, List<DeckEntry> deck)
        {
            if (File.Exists(path)) File.Delete(path);

            XmlSerializer xms = new XmlSerializer(typeof(List<DeckEntry>));

            using (Stream outfile = File.OpenWrite(path))
            {
                xms.Serialize(outfile, deck);
            }
        }

        public List<DeckEntry> loadDeck(string path)
        {
            try
            {
                if (!File.Exists(path))
                {
                    return null;
                }
                List<DeckEntry> lde = new List<DeckEntry>();

                using (Stream infile = File.OpenRead(path))
                {
                    XmlSerializer xmls2 = new XmlSerializer(typeof(List<DeckEntry>));
                    lde = (List<DeckEntry>)xmls2.Deserialize(infile);
                }

                return lde;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.StackTrace, ex.Message);
            }
            return null;
        }

        private void setDeck(List<DeckEntry> deck)
        {
            dataGridView1.Rows.Clear();
            foreach (DeckEntry sss in deck)
            {
                addListItem(sss.CardName, sss.Quantity);
            }

        }


        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start(linkLabel1.Text);
        }

        private void dataGridView1_SelectionChanged(object sender, EventArgs e)
        {
            if (dataGridView1.SelectedRows.Count > 0)
            {
                string cardName = (string)dataGridView1.SelectedRows[0].Cells[0].Value;
                //Image selectedCard = getCardPicture(cardName);
                if (!string.IsNullOrEmpty(cardName))
                {
                    linkLabel1.Text = "http://magiccards.info/query?q=!" + cardName;
                }
                else linkLabel1.Text = "";
                // else setCardPicture(MtgPrinter.Properties.Resources.nopicture);*/

                getCardPictureAsync(cardName);
            }
        }

        private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            lblKart.Text = getTotalNumCards().ToString();
            if (e != null && e.ColumnIndex == 0)
            {
                if (dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value != null)
                {
                    string newCardName = (string)dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
                    newCardName = CultureInfo.GetCultureInfo("en").TextInfo.ToTitleCase(newCardName.Trim());
                    dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = newCardName;
                    if (dataGridView1.Rows[e.RowIndex].Cells[1].Value == null) dataGridView1.Rows[e.RowIndex].Cells[1].Value = 1;

                    getCardPictureAsync(newCardName);
                }
            }
        }

        private void newToolStripButton_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Sure?", "New Deck?", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                dataGridView1.Rows.Clear();
            }
        }

        private int getTotalNumCards()
        {
            int sum = 0;
            foreach (DataGridViewRow row in dataGridView1.Rows)
            {
                if (row.Cells.Count > 1 && row.Cells[1].Value != null)
                {
                    int ile;
                    if (int.TryParse(row.Cells[1].Value.ToString(), out ile))
                        //int ile = int.Parse(row.Cells[1].Value.ToString());
                        sum += ile;
                    else row.Cells[1].Value = 0;
                }
            }
            return sum;
        }

        private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            lblKart.Text = getTotalNumCards().ToString();

        }

        private void button2_Click_1(object sender, EventArgs e)
        {
            if (dataGridView1.SelectedRows[0].Cells[0].Value == null) return;
            string cardname = (string)dataGridView1.SelectedRows[0].Cells[0].Value;

            cardname = cardname.Replace(' ', '+');
            string url = "http://flamberg.com.pl/advanced_search_result.php?keywords=" + cardname;
            System.Diagnostics.Process.Start(url);
        }

        private void dataGridView1_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
        {
            lblKart.Text = getTotalNumCards().ToString();
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "Jpg files (*.jpg)|*.jpg|All files (*.*)|*.*";
            dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            dialog.Title = "Gdzie zapisac obraz";
            //dialog.FileName = imagename;
            //TODO
            string strFileName = string.Empty;
            var result = dialog.ShowDialog();
            if (result == DialogResult.OK)
                strFileName = dialog.FileName;
            if (result != DialogResult.OK || strFileName == String.Empty) return;


            pictureBox1.Image.Save(strFileName);
        }

        #region ToolStrip Methods
        private void saveToolStripButton_Click(object sender, EventArgs e)
        {
            using (SaveFileDialog dialog = new SaveFileDialog())
            {
                dialog.Filter = "MtgDeck files (*.mtgdeck)|*.mtgdeck|All files (*.*)|*.*";
                dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                dialog.Title = "Gdzie zapisac Talie";
                dialog.FileName = "MyDeck.mtgdeck";
                string strFileName = string.Empty;

                var result = dialog.ShowDialog();
                if (result == DialogResult.OK)
                    strFileName = dialog.FileName;
                if (result != DialogResult.OK || strFileName == String.Empty) return;

                saveDeck(strFileName, parseData());
                AddRecentFileToMenu(strFileName);
            }
        }

        private void openToolStripButton_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog dialog = new OpenFileDialog())
            {
                dialog.Filter = "MtgDeck files (*.mtgdeck)|*.mtgdeck|All files (*.*)|*.*";
                dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                dialog.Title = "Plik z Talia";
                string strFileName = string.Empty;

                var result = dialog.ShowDialog();
                if (result == DialogResult.OK)
                    strFileName = dialog.FileName;
                if (result != DialogResult.OK || strFileName == String.Empty) return;

                setDeck(loadDeck(strFileName));
                AddRecentFileToMenu(strFileName);
            }
        }
        #endregion

        #region Recent Files Methods
        private void LoadRecentFilesList()
        {
            if (!File.Exists(getWorkpath() + "recent.xml")) return;

            XmlSerializer xmlser = new System.Xml.Serialization.XmlSerializer(typeof(List<string>));
            using (Stream recentFile = File.OpenRead(getWorkpath() + "recent.xml"))
            {
                object result = xmlser.Deserialize(recentFile);
                if (result != null) recentDecks = result as List<string>;
                else return;
            }
            UpdateRecentFilesMenu();
        }

        private void SaveRecentFilesList()
        {
            string filename = getWorkpath() + "recent.xml";
            if (File.Exists(filename)) File.Delete(filename);

            XmlSerializer xmlser = new System.Xml.Serialization.XmlSerializer(typeof(List<string>));

            using (Stream recentFile = File.OpenWrite(filename))
            {
                xmlser.Serialize(recentFile, recentDecks);
            }
        }

        private void UpdateRecentFilesMenu()
        {
            this.recentToolStripMenuItem.DropDownItems.Clear();
            foreach (string str in recentDecks)
            {
                this.recentToolStripMenuItem.DropDownItems.Add(str);
            }
        }

        private void AddRecentFileToMenu(string filepath)
        {
            if (recentDecks.Contains(filepath))
            {
                recentDecks.Remove(filepath);
            }
            recentDecks.Insert(0, filepath);
            if (recentDecks.Count > 8) recentDecks = recentDecks.GetRange(0, 8);
            UpdateRecentFilesMenu();
        }

        private void RemoveRecentFileFromMenu(string filepath)
        {
            if (recentDecks.Contains(filepath))
            {
                recentDecks.Remove(filepath);
            }
            UpdateRecentFilesMenu();
        }

        private void recentToolStripMenuItem_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            string filename = e.ClickedItem.Text;
            if (File.Exists(filename))
            {
                setDeck(loadDeck(filename));
                AddRecentFileToMenu(filename);
                UpdateRecentFilesMenu();
            }
            else
            {
                RemoveRecentFileFromMenu(filename);
                MessageBox.Show("Can't find file", "Can't find file", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        #endregion

        #region Menu Items Methods
        private void parseDeckToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (ParseDeckForm pdform = new ParseDeckForm())
            {
                DialogResult dr = pdform.ShowDialog();
                if (dr == System.Windows.Forms.DialogResult.OK)
                {
                    parseAlot(pdform.GetDeckText());
                }
            }
        }

        private void statisticsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (StatisticsForm sf = new StatisticsForm())
            {
                sf.ShowDialog();
            }
        }

        private void quitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void openDeckToolStripMenuItem_Click(object sender, EventArgs e)
        {
            openToolStripButton_Click(sender, e); //bad coding yea
        }

        private void printToolStripButton_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "Html files (*.html)|*.html|All files (*.*)|*.*";
            dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            dialog.Title = "Gdzie zapisac Htmla do wydruku";
            dialog.FileName = "MyDeck.html";
            string strFileName = string.Empty;

            var result = dialog.ShowDialog();
            if (result == DialogResult.OK)
                strFileName = dialog.FileName;
            if (result != DialogResult.OK || strFileName == String.Empty) return;

            Exporters.exportToHtml(strFileName, parseData(), this.CreateGraphics(), null);
            System.Diagnostics.Process.Start(strFileName);
        }

        private void toPrintableHtmlToolStripMenuItem_Click(object sender, EventArgs e)
        {
            printToolStripButton_Click(sender, e);
        }

        private void toTextFileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "Html files (*.txt)|*.txt|All files (*.*)|*.*";
            dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            dialog.Title = "Gdzie zapisac plik tekstowy";
            dialog.FileName = "MyDeck.txt";
            string strFileName = string.Empty;

            var result = dialog.ShowDialog();
            if (result == DialogResult.OK)
                strFileName = dialog.FileName;
            if (result != DialogResult.OK || strFileName == String.Empty) return;

            Exporters.exportToTxt(strFileName, parseData());
            System.Diagnostics.Process.Start(strFileName);
        }

        private void toPdfToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "Pdf files (*.pdf)|*.pdf|All files (*.*)|*.*";
            dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            dialog.Title = "Where to write the pdf file";
            dialog.FileName = "MyDeck.pdf";
            string strFileName = string.Empty;

            var result = dialog.ShowDialog();
            if (result == DialogResult.OK)
                strFileName = dialog.FileName;
            if (result != DialogResult.OK || strFileName == String.Empty) return;

            BackgroundWorker bwrk = new BackgroundWorker();
            bwrk.WorkerReportsProgress = true;

            ProgressDialog pdlg = new ProgressDialog();
            pdlg.Show(this);

            bwrk.DoWork += new DoWorkEventHandler(delegate(object argument, DoWorkEventArgs a)
            {
                Exporters.exportToPdf(strFileName, parseData(), Program.AppConfig.PdfPageSize, delegate(float percent)
                {
                    bwrk.ReportProgress((int)(percent * 100));
                });
            });

            bwrk.ProgressChanged += new ProgressChangedEventHandler(delegate(object ob, ProgressChangedEventArgs a)
            {
                pdlg.SetProgressValue(a.ProgressPercentage);
            });

            bwrk.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate(object w, RunWorkerCompletedEventArgs a)
            {
                pdlg.Close();
                pdlg.Dispose();

                System.Diagnostics.Process.Start(strFileName);
            });
            bwrk.RunWorkerAsync(strFileName);
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (SettingsForm sf = new SettingsForm(Program.AppConfig))
            {
                sf.ShowDialog();
                ReadConfiguration();
            }
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (dataGridView1.Rows.Count == 1 || MessageBox.Show("New Deck?", "Sure?", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                dataGridView1.Rows.Clear();
            }
        }

        private void saveToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            saveToolStripButton_Click(sender, e);
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (AboutBox1 abx = new AboutBox1())
            {
                abx.ShowDialog();
            }
        }
        #endregion

        private void dataGridView1_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            lblKart.Text = getTotalNumCards().ToString();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            int max = dataGridView1.Rows.Count;
            Random rnd = new Random(DateTime.Now.Millisecond);
            var a = dataGridView1.Rows[rnd.Next(max - 1)];
            getCardPictureAsync(a.Cells[0].Value as string);

        }
    }
}
